/*
 *      Copyright 2008, M.B.I. srl.
 * 
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

#ifndef PYOSI_COMMON
#define PYOSI_COMMON

#include <boost/python.hpp>

#ifndef NDEBUG
#include <iostream>
#include <string>


#define FUNCTION_NAME_MACRO  __FUNCTION__
#define LOG_STREAM std::cerr << "pyOSI : DEBUG : "
#define LOG_CALL LOG_STREAM << FUNCTION_NAME_MACRO << std::endl;
#endif

typedef unsigned int arraySize;

// const_ptr_Wrap
/*
 * Class to wrap the return value of some OsiSolverInterface methods
 * similar to a read-only fixed dimension array. The ownership of the
 * pointed memory remains to the library
 */

template<class T>
class const_ptr_Wrap{
    public:    
    const_ptr_Wrap() : thePtr(0), theIterator(0), theSize(0){
        #ifndef NDEBUG
        LOG_CALL
        #endif
    }
    const_ptr_Wrap( const T * aPtr , arraySize aSize) :  thePtr(aPtr), theIterator(0), theSize(aSize){
        #ifndef NDEBUG
        LOG_CALL
        LOG_STREAM << aPtr << " " << aSize << std::endl;
        if ((aPtr == 0 )&& (aSize != 0))
        LOG_STREAM << "Warning: const NULL pointer array!!" << std::endl; 
        #endif
    }
    arraySize length() const {
        #ifndef NDEBUG
        LOG_CALL
        #endif
        return theSize;
    } 
    const T & getItem(arraySize aIndex) const {
        #ifndef NDEBUG
        LOG_CALL
        if ( aIndex >= theSize )
            throw std::out_of_range("Index out of range");
        #endif
        return thePtr[aIndex];
    }
    boost::python::str repr() const {
        std::ostringstream out;
        out  << "[";
        for ( arraySize i = 0; i < theSize ; ) 
        {
            out << thePtr[i];
            if ( ++i == theSize ) 
                out << " ]";
            else
                out << " ,";
        }       
        return out.str().c_str();
    }
    const T & next() const{
        #ifndef NDEBUG
        LOG_CALL
        #endif
        if ( theIterator >= theSize ) {
            theIterator = 0;
            PyErr_SetNone(PyExc_StopIteration);
            throw_error_already_set();
        }
        return thePtr[theIterator++];
    }
    const const_ptr_Wrap<T> & iter() const{
        return *this;
    }
    
    private:
    const T * thePtr;
    mutable arraySize theIterator;
    arraySize theSize;
};

// ptr_Wrap
/*
 * Class to wrap some OsiSolverInterface methods args
 * similar to a mutable fixed dimension array. The ownership of the
 * pointed memory remain to python evenif in the module memory space
 * Some methods may aquire the ownership turning to false the flag
 * attribute. 
 */
template<class T>
class ptr_Wrap{
    public:
    ptr_Wrap() : thePtr(0), theSize(0), theFlag(false) {
        #ifndef NDEBUG
        LOG_CALL
        #endif
    }
    ptr_Wrap( arraySize aSize ) : thePtr(new T[aSize]), theSize(aSize), theFlag(true) {
        #ifndef NDEBUG
        LOG_CALL
        LOG_STREAM << thePtr << std::endl;
        #endif
    }
    ~ptr_Wrap(){
        #ifndef NDEBUG
        LOG_CALL
        #endif      
        if ( theFlag ) 
        {
            #ifndef NDEBUG
            LOG_STREAM << thePtr << " deleted[]" << std::endl;
            #endif
            delete[] thePtr;
        }
    }
    void fill( const T & aT ){
        #ifndef NDEBUG
        LOG_CALL
        #endif
        for( arraySize i = 0; i < theSize ; thePtr[i++] = aT); 
    }
    arraySize length() const {
        #ifndef NDEBUG
        LOG_CALL
        #endif
        return theSize;
    }
    const T * ptr() const {
        #ifndef NDEBUG
        LOG_CALL
        #endif
        return thePtr;
    }
    T * ptr() {
        #ifndef NDEBUG
        LOG_CALL
        #endif
        return thePtr;
    }
    const T & getItem (arraySize aIndex ) const {
        #ifndef NDEBUG
        LOG_CALL
        if ( aIndex >= theSize )
            throw std::out_of_range("Index out of range");
        #endif      
        
        return thePtr[aIndex];
    }
    void setItem(arraySize aIndex, const T & aValue) {
        #ifndef NDEBUG
        LOG_CALL
        if ( aIndex >= theSize )
            throw std::out_of_range("Index out of range");
        #endif      
        thePtr[aIndex] = aValue;
    }
    boost::python::str repr() const {
        std::ostringstream out;
        out  << "[";
        for ( arraySize i = 0; i < theSize ; ) 
        {
            out << thePtr[i];
            if ( ++i == theSize ) 
                out << " ]";
            else
                out << " ,";
        }       
        return out.str().c_str();
    }
    
    private:
    T * thePtr;
    arraySize theSize;
    bool theFlag;
    
};

typedef ptr_Wrap<int> ptr_int;
typedef ptr_Wrap<char> ptr_char;
typedef ptr_Wrap<double> ptr_double;
typedef const_ptr_Wrap<int> cptr_int;
typedef const_ptr_Wrap<char> cptr_char;
typedef const_ptr_Wrap<double> cptr_double;

#endif
